package logger

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"
)

// 日志级别的优先级从低到高为：
// ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
// 当设置某个级别时，低于该级别的日志信息将不会被记录
const (
	loglevelDebug = 1 << iota
	loglevelInfo
	loglevelWarn
	loglevelError
)

// 默认日志级别为 INFO
var logLevel = loglevelInfo

func init() {
	// 获取参数，判断是否需要打印 debug
	allArgs := os.Args[1:]
outOfSetLevel:
	for _, arg := range allArgs {
		switch strings.ToLower(arg) {
		case "debug":
			logLevel = loglevelDebug
			Infof("日志级别:%s\n", arg)
			break outOfSetLevel
		case "info":
			logLevel = loglevelInfo
			Infof("日志级别:%s\n", arg)
			break outOfSetLevel
		case "warn":
			logLevel = loglevelWarn
			Infof("日志级别:%s\n", arg)
			break outOfSetLevel
		case "error":
			logLevel = loglevelError
			Infof("日志级别:%s\n", arg)
			break outOfSetLevel
		}
	}
}

// 是否已经初始化
var initFlag = false

type LogConfigStruct struct {
	LogDir        string `name:"日志目录"`
	LogNamePrefix string `name:"日志文件前缀"`
}

func InitLog(config *LogConfigStruct) (*os.File, error) {
	if initFlag {
		Errorf("日志已初始化")
		return nil, errors.New("日志已初始化")
	}
	defer func() {
		Infof("日志初始化完成")
		initFlag = true
	}()

	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
	Infof("初始化日志\n")

	if config == nil {
		config = &LogConfigStruct{
			LogDir:        "./log",
			LogNamePrefix: "yuque-",
		}
	}

	finalPatchDir, err := createPatchDir(config)
	if err != nil {
		Errorf("日志初始化失败: %v", err)
		return nil, err
	}

	logFile, err := createPatchFile(finalPatchDir, config)
	if err != nil {
		Errorf("日志初始化失败: %v", err)
		return nil, err
	}
	return logFile, nil
}

// myFileStruct 重写 os.File 的 Write 方法
type myFileStruct struct {
	file       *os.File         `name:"输出日志文件"`
	delRegexps []*regexp.Regexp `name:"删除字符的正则"`
	lock       sync.Mutex       `name:"上锁"`
}

func (m *myFileStruct) Write(p []byte) (int, error) {
	m.lock.Lock()
	// 对写入日志文件的记录进行特殊处理
	s := string(p)
	// 根据正则去除字符
	if m.delRegexps != nil && len(m.delRegexps) > 0 {
		for _, re := range m.delRegexps {
			s = re.ReplaceAllString(s, "")
		}
	}
	p = []byte(s)
	n, err := m.file.Write(p)
	m.lock.Unlock()
	return n, err
}

// 输出日志定位到文件，创建补丁文件
func createPatchFile(finalPatchDir string, config *LogConfigStruct) (*os.File, error) {
	// 创建或打开一个文件
	now := time.Now()
	formatted := now.Format("20060102_150405")
	logFileName := fmt.Sprintf("%s_%s.log", config.LogNamePrefix, formatted)
	logFilePath := filepath.Join(finalPatchDir, logFileName)
	logFile, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		Errorf("error opening file: %v", err)
		return nil, errors.Join(fmt.Errorf("error opening file"), err)
	}

	re, _ := regexp.Compile("(\u001B)?\\[\\d{1,2}m")
	myFile := &myFileStruct{
		file:       logFile,
		delRegexps: []*regexp.Regexp{re},
		lock:       sync.Mutex{},
	}

	// 创建一个 io.MultiWriter 实例，它允许你将输出写入多个地方
	multiWriter := io.MultiWriter(os.Stdout, myFile)

	// 设置日志输出为 multiWriter
	log.SetOutput(multiWriter)

	Infof("创建日志文件: %s\n", logFilePath)

	return logFile, nil
}

// 创建补丁目录
func createPatchDir(config *LogConfigStruct) (string, error) {
	patchPath := config.LogDir

	finalPatchPath := filepath.Join(patchPath)
	err := os.MkdirAll(finalPatchPath, 0755)
	if err != nil {
		Errorf("创建补丁目录报错!: %v", err.Error())
		return "", fmt.Errorf("创建补丁目录报错!: %v", err.Error())
	}
	return finalPatchPath, nil
}

type LogFormatterParams struct {
	// 日志时间戳
	TimeStamp time.Time
	// 级别
	Level string
	// 日志内容
	Format string
}

var defaultLogFormatter = func(param LogFormatterParams, v []interface{}) string {

	if !strings.HasSuffix(param.Format, "\n") {
		param.Format += "\n"
	}
	// if _, ok := v.([]interface{}); ok && len(v.([]interface{})) > 0 {
	param.Format = fmt.Sprintf(param.Format, v...)
	// }

	return fmt.Sprintf("[%s] %v %s",
		param.Level,
		param.TimeStamp.Format("2006/01/02 - 15:04:05"),
		param.Format,
	)
}

func Infof(format string, v ...any) {
	if logLevel > loglevelInfo {
		return
	}
	fmt.Fprint(
		log.Writer(),
		defaultLogFormatter(LogFormatterParams{
			Level:     "INFO",
			Format:    format,
			TimeStamp: time.Now(),
		}, v),
	)
}

func Errorf(format string, v ...any) {
	if logLevel > loglevelError {
		return
	}
	fmt.Fprint(
		log.Writer(),
		defaultLogFormatter(LogFormatterParams{
			Level:     "ERROR",
			Format:    format,
			TimeStamp: time.Now(),
		}, v),
	)
}

func Debugf(format string, v ...any) {
	if logLevel > loglevelDebug {
		return
	}
	fmt.Fprint(
		log.Writer(),
		defaultLogFormatter(LogFormatterParams{
			Level:     "DEBUG",
			Format:    format,
			TimeStamp: time.Now(),
		}, v),
	)
}

func Warnf(format string, v ...any) {
	if logLevel > loglevelWarn {
		return
	}
	fmt.Fprint(
		log.Writer(),
		defaultLogFormatter(LogFormatterParams{
			Level:     "WARN",
			Format:    format,
			TimeStamp: time.Now(),
		}, v),
	)
}
